So, you want to allow unauthenticated, anonymous remote users to upload content onto your website? Before grabbing Joes Image Hosting Script v1.4 you should be aware of the security risks and thoroughly test any script before allowing it to be used by the public. A large number of publicly available file upload scripts can leave your server vulnerable to attack.
These are the main issues you should be aware of when designing or implementing any kind of dynamic page that accepts user supplied files for upload and subsequently accessible for download.
Finding the File Name
It is up to the client to supply to your script the name of the file the user has just uploaded so you should treat it with caution. A user may craft a filename containing path-traversal mechanisms such as "../../myimage.jpg". You must ensure that the filename is stripped down to its base name to avoid any potential path traversal exploits.
Different web browsers will provide you with the name of the uploaded file in different ways. Microsoft Internet Explorer likes to give you the full path, such as "C:\Documents and Settings\Administrator\Desktop\uploadedfile.jpg" whereas the Mozilla family of browsers will just supply "uploadedfile.jpg".
You should also ensure that the filename consists of characters that are valid for the file system on which the image will eventually be saved. On the Linux ext3 file system, a colon is legal in a filename but is not legal on the FAT32 or NTFS file systems.
Overwriting
Simply - if a user uploads a file with the same name as an existing file, is it overwritten or does the upload fail? Perhaps the file should be automatically renamed to avoid the user having to upload it again. If you do allow for overwriting of existing files, you should ensure that your script that handles the uploading is in a separate directory than where the uploaded files are stored or it may be overwritten itself!
File Extensions Validation and Directory Protection
Does your script prevent people uploading dynamic content such as files ending in .php, .cgi, .shtml, etc? Ideally you should disable all types of server-side scripting support in the directory designated for user uploads and use access restrictions to only allow access to the file types your script is configured to accept. Here is a sample .htaccess file you may wish to place in your upload directory if you do not have permission to do such from the server config. Don't forget to edit the allowed types to the file types you wish to accept.
#Disable rewriting (incompatible with Options -All)
RewriteEngine off
#Disable PHPs script execution engine
php_flag engine 0
php_flag engine 0
#Disable all CGI and SSI and other dangerous options
Options -All -MultiViews
#Deny everything by default
Order Allow,Deny
#Only allow these types of file
Order Deny,Allow
Allow from all
#Deny non-GET requests
Order Allow,Deny
You should not rely on the "Content-Type" field of uploaded files to restrict which formats are accepted. Since this field is supplied by the client, it is trivially bypassed, for example, a client could upload a file called virus.exe and supply a Content-Type of image/jpeg. It is generally a good idea to ignore the Content-Type field supplied by the client altogether as some browsers do not generate it correctly.
Double-barelled File Extensions
A seemingly unknown "feature" in the Apache web server is that the mod_mime code will scan a file and match on ANY extension. A file called "myimage.cgi.jpg" will invoke the cgi-script handler if enabled which is far from the expected behaviour. Thankfully this will not allow remote code execution since the file would need to be chmod +x for it to be invoked as a CGI.
PHP however is not so lucky. PHP support is often configured by the Apache web server as a MIME type such that any file with the "application/x-httpd-php" MIME type is executed as a PHP script. Thus, if a file named myimage.php.foo is uploaded, and "foo" does not have an associated MIME type, PHPs MIME type will be used and thus the file will be executed as PHP code! Where a valid MIME type exists, it will be used instead, so myimage.php.jpg will instead be treated as "image/jpeg" assuming jpg has the associated MIME type (it does by default). A similar problem occurs when PHP is registered as a handler - in this case, any filename containing the .php extension will be executed. Again, care should be taken when checking the file extension as I have seen some vulnerable scripts accept a file called image.php.jjjpg and since jjjpg wasn't a registered MIME type, arbitrary code execution occured.
A popular control panel product, cPanel, often runs PHP as a CGI to aid in auditing and permissions issues. The cPanel developers though have PURPOSEFULLY REMOVED the +x requirement for PHPs running as CGI scripts, allowing remote code execution as above! When emailed, the cPanel developers responded that the change was intentional to improve usability. Such basic security should never be sacrificed in the name of not having to chmod +x a file.
When deploying a file uploading script, be sure that it strips out these double-barelled extensions and only saves the file as "filename.ext" on the server. If you have access, you can apply a patch to Apache to remove the handler behaviour and only take into account the extension at the end of the filename. I have been using this patch in production use with no issues. Note that this patch will ONLY work if PHP is configured as a handler via "AddHandler application/x-httpd-php .php" in the httpd.conf.
Content Type Validation
It's all very well to say you only support uploading of GIF, PNG and JPEG files by checking the file extension, but what is stopping a malicious user renaming an MP3 or EXE file to .jpg and uploading it to your site? Content type validation should be used to ensure that a file with a .jpg extension is really a file complying to the JPEG file format. How this is implemented will vary from language to language but here is some sample PHP code that performs simple header validation on a number of image formats. Note that you should not consider this kind of validation exhaustive - someone could for example add a JPEG or PNG header onto an existing file and bypass this kind of check, but it is a good way to prevent simple renaming and uploading of MP3s and other content that you may not desire.
Viruses and Exploits
In 2004 a vulnerability was discovered in how certain applications process JPEG files. A maliciously constructed JPEG file could be used to cause arbitrary code execution on the victim's machine simply by viewing it. It may be wise to employ a virus scanner such as ClamAV to check uploaded content is free of viruses or exploits before allowing it to be served again to other users.
Storage Exhaustion
Where is the uploaded file actually going whilst it is being transferred? This is often the job of the web server to manage, and is usually some temporary file on a hard disk. You should ensure that the web server has appropriate limits in place to prevent someone from uploading an unusually large file and filling up your temporary storage. With the Apache web server, this can be controlled by the LimitRequestBody directive.
Compressed Archives
If you allow a user to upload an archive of files, for example, a .zip file, be sure to take extra care when extracting the contents. Depending on the archive type and decompressor, you will need to check for things such as ../ paths, permission/setuid bits, filenames, overwriting, illegal directory structures (eg a zip with self-referencing directories could eat up 100% CPU time), and other such hazards. After extracting files from an archive, be sure to follow all the other precautions listed in this article for each file.